/**
 * Agent Configuration Manager
 * 
 * Manages CRUD operations for agent configurations
 */

import type {
  AgentConfig,
  CreateAgentRequest,
  UpdateAgentRequest,
  AgentListResponse,
  WorkflowValidationResult,
  AgentTemplate,
} from '../types/agent-config';

/**
 * Agent Configuration Manager
 */
export class AgentConfigManager {
  private agents: Map<string, AgentConfig> = new Map();
  private templates: Map<string, AgentTemplate> = new Map();

  constructor() {
    this.loadDefaultTemplates();
  }

  /**
   * Create a new agent configuration
   */
  async createAgent(request: CreateAgentRequest): Promise<AgentConfig> {
    const id = crypto.randomUUID();
    const now = Date.now();

    const agent: AgentConfig = {
      id,
      name: request.name,
      description: request.description,
      systemPrompt: request.systemPrompt,
      model: request.model,
      tools: request.tools || [],
      subagents: request.subagents || [],
      workflow: request.workflow || { nodes: [], edges: [] },
      settings: {
        contextStrategy: 'compact',
        maxTurns: 10,
        includePartialMessages: false,
        ...request.settings,
      },
      createdAt: now,
      updatedAt: now,
      version: 1,
    };

    // Validate workflow
    const validation = this.validateWorkflow(agent.workflow);
    if (!validation.valid) {
      throw new Error(`Invalid workflow: ${validation.errors.map(e => e.message).join(', ')}`);
    }

    this.agents.set(id, agent);
    return agent;
  }

  /**
   * Get an agent configuration by ID
   */
  async getAgent(id: string): Promise<AgentConfig | null> {
    return this.agents.get(id) || null;
  }

  /**
   * List all agent configurations
   */
  async listAgents(page: number = 1, pageSize: number = 20): Promise<AgentListResponse> {
    const allAgents = Array.from(this.agents.values());
    const start = (page - 1) * pageSize;
    const end = start + pageSize;
    const agents = allAgents.slice(start, end);

    return {
      agents,
      total: allAgents.length,
      page,
      pageSize,
    };
  }

  /**
   * Update an agent configuration
   */
  async updateAgent(id: string, request: UpdateAgentRequest): Promise<AgentConfig> {
    const agent = this.agents.get(id);
    if (!agent) {
      throw new Error(`Agent not found: ${id}`);
    }

    const updatedAgent: AgentConfig = {
      ...agent,
      ...request,
      id, // Preserve ID
      createdAt: agent.createdAt, // Preserve creation time
      updatedAt: Date.now(),
      version: agent.version + 1,
      workflow: request.workflow || agent.workflow,
      settings: {
        ...agent.settings,
        ...request.settings,
      },
    };

    // Validate workflow if updated
    if (request.workflow) {
      const validation = this.validateWorkflow(updatedAgent.workflow);
      if (!validation.valid) {
        throw new Error(`Invalid workflow: ${validation.errors.map(e => e.message).join(', ')}`);
      }
    }

    this.agents.set(id, updatedAgent);
    return updatedAgent;
  }

  /**
   * Delete an agent configuration
   */
  async deleteAgent(id: string): Promise<boolean> {
    return this.agents.delete(id);
  }

  /**
   * Duplicate an agent configuration
   */
  async duplicateAgent(id: string, newName?: string): Promise<AgentConfig> {
    const agent = this.agents.get(id);
    if (!agent) {
      throw new Error(`Agent not found: ${id}`);
    }

    const newId = crypto.randomUUID();
    const now = Date.now();

    const duplicatedAgent: AgentConfig = {
      ...agent,
      id: newId,
      name: newName || `${agent.name} (Copy)`,
      createdAt: now,
      updatedAt: now,
      version: 1,
    };

    this.agents.set(newId, duplicatedAgent);
    return duplicatedAgent;
  }

  /**
   * Validate workflow structure
   */
  validateWorkflow(workflow: AgentConfig['workflow']): WorkflowValidationResult {
    const errors: WorkflowValidationResult['errors'] = [];
    const warnings: WorkflowValidationResult['warnings'] = [];

    // Check if workflow has nodes
    if (workflow.nodes.length === 0) {
      warnings.push({
        message: 'Workflow has no nodes',
      });
      return { valid: true, errors, warnings };
    }

    // Check for input node
    const inputNodes = workflow.nodes.filter(n => n.type === 'input');
    if (inputNodes.length === 0) {
      errors.push({
        message: 'Workflow must have at least one input node',
      });
    } else if (inputNodes.length > 1) {
      warnings.push({
        message: 'Workflow has multiple input nodes',
      });
    }

    // Check for output node
    const outputNodes = workflow.nodes.filter(n => n.type === 'output');
    if (outputNodes.length === 0) {
      warnings.push({
        message: 'Workflow has no output node',
      });
    }

    // Check for orphaned nodes (nodes with no connections)
    const connectedNodeIds = new Set<string>();
    workflow.edges.forEach(edge => {
      connectedNodeIds.add(edge.source);
      connectedNodeIds.add(edge.target);
    });

    workflow.nodes.forEach(node => {
      if (!connectedNodeIds.has(node.id) && workflow.nodes.length > 1) {
        warnings.push({
          nodeId: node.id,
          message: `Node "${node.data.label}" is not connected`,
        });
      }
    });

    // Check for invalid edge references
    const nodeIds = new Set(workflow.nodes.map(n => n.id));
    workflow.edges.forEach(edge => {
      if (!nodeIds.has(edge.source)) {
        errors.push({
          edgeId: edge.id,
          message: `Edge references non-existent source node: ${edge.source}`,
        });
      }
      if (!nodeIds.has(edge.target)) {
        errors.push({
          edgeId: edge.id,
          message: `Edge references non-existent target node: ${edge.target}`,
        });
      }
    });

    // Check for cycles (simple detection)
    const hasCycle = this.detectCycle(workflow);
    if (hasCycle) {
      warnings.push({
        message: 'Workflow contains cycles - ensure loop nodes are used correctly',
      });
    }

    return {
      valid: errors.length === 0,
      errors,
      warnings,
    };
  }

  /**
   * Detect cycles in workflow (simple DFS-based detection)
   */
  private detectCycle(workflow: AgentConfig['workflow']): boolean {
    const adjacencyList = new Map<string, string[]>();
    
    // Build adjacency list
    workflow.edges.forEach(edge => {
      if (!adjacencyList.has(edge.source)) {
        adjacencyList.set(edge.source, []);
      }
      adjacencyList.get(edge.source)!.push(edge.target);
    });

    const visited = new Set<string>();
    const recursionStack = new Set<string>();

    const dfs = (nodeId: string): boolean => {
      visited.add(nodeId);
      recursionStack.add(nodeId);

      const neighbors = adjacencyList.get(nodeId) || [];
      for (const neighbor of neighbors) {
        if (!visited.has(neighbor)) {
          if (dfs(neighbor)) {
            return true;
          }
        } else if (recursionStack.has(neighbor)) {
          return true; // Cycle detected
        }
      }

      recursionStack.delete(nodeId);
      return false;
    };

    for (const node of workflow.nodes) {
      if (!visited.has(node.id)) {
        if (dfs(node.id)) {
          return true;
        }
      }
    }

    return false;
  }

  /**
   * Load default agent templates
   */
  private loadDefaultTemplates(): void {
    // Customer Service Agent Template
    this.templates.set('customer-service', {
      id: 'customer-service',
      name: 'Customer Service Agent',
      description: 'A helpful customer service agent that can answer questions and resolve issues',
      category: 'support',
      tags: ['customer-service', 'support', 'helpdesk'],
      isPublic: true,
      config: {
        name: 'Customer Service Agent',
        description: 'Helpful customer service agent',
        systemPrompt: {
          type: 'text',
          content: 'You are a helpful customer service agent. Be polite, professional, and solve customer issues efficiently.',
        },
        model: 'claude-sonnet-4',
        tools: [],
        subagents: [],
        workflow: { nodes: [], edges: [] },
        settings: {
          contextStrategy: 'compact',
          maxTurns: 10,
          temperature: 0.7,
        },
      },
    });

    // Add more templates as needed
  }

  /**
   * Get all templates
   */
  async getTemplates(): Promise<AgentTemplate[]> {
    return Array.from(this.templates.values());
  }

  /**
   * Create agent from template
   */
  async createFromTemplate(templateId: string, name?: string): Promise<AgentConfig> {
    const template = this.templates.get(templateId);
    if (!template) {
      throw new Error(`Template not found: ${templateId}`);
    }

    return this.createAgent({
      ...template.config,
      name: name || template.config.name,
    });
  }
}

// Singleton instance
export const agentConfigManager = new AgentConfigManager();

